package com.gaofans.vertx.boot.web.server;

import com.gaofans.vertx.boot.common.verticle.Verticle;
import com.gaofans.vertx.boot.web.config.VertxWebAutoConfig;
import com.gaofans.vertx.boot.web.config.FreemarkerTemplateEngineAutoConfig;
import com.gaofans.vertx.boot.web.route.RouteDefinition;
import com.gaofans.vertx.boot.web.route.RouteDiscoverer;
import com.gaofans.vertx.boot.web.route.RouterConfigurer;
import com.gaofans.vertx.boot.web.route.RouterExtConfigurer;
import com.gaofans.vertx.boot.web.session.SessionHandlerAutoConfig;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;

import java.util.Collection;
import java.util.List;

import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;

/**
 * HttpServerVerticle
 * @author GaoFans
 */
@Verticle(handler = HttpServerResultHandlerBuilder.class,
        options = HttpServerDeploymentOptionsBuilder.class,
        scopeName = SCOPE_PROTOTYPE)
@AutoConfigureAfter({VertxWebAutoConfig.class,
        FreemarkerTemplateEngineAutoConfig.class,
        SessionHandlerAutoConfig.class,
        SessionHandlerAutoConfig.class})
public class HttpServerVerticle extends AbstractVerticle {

    private HttpServerOptions options;
    private HttpServer server;
    private RouteDiscoverer routeDiscoverer;
    private List<RouterConfigurer> configurers;
    private List<RouterExtConfigurer> extConfigurers;
    private final static Logger LOG = LoggerFactory.getLogger(HttpServerVerticle.class);

    public HttpServerVerticle(HttpServerOptions options,
                              RouteDiscoverer routeDiscoverer,
                              List<RouterConfigurer> configurers,
                              List<RouterExtConfigurer> extConfigurers) {
        this.options = options;
        this.routeDiscoverer = routeDiscoverer;
        this.configurers = configurers;
        this.extConfigurers = extConfigurers;
    }

    @Override
    public void start(Promise<Void> startPromise) {
        HttpServer localServer = vertx.createHttpServer(options);
        Router router = Router.router(vertx);
        Collection<RouteDefinition> routes = routeDiscoverer.findRoutes();
        if(routes.size() == 0){
            router.route().handler(routingContext -> {
                HttpServerResponse response = routingContext.response();
                response.putHeader("content-type", "text/plain");
                response.end("Hello World from Vert.x-Web!");
            });
        }else{
            for (RouteDefinition definition : routes) {
                Route route = router.route();
                for (RouterConfigurer configurer : configurers) {
                    configurer.accept(definition, route);
                }
            }
            for (RouterExtConfigurer extConfigurer : extConfigurers) {
                extConfigurer.accept(router);
            }
        }

        localServer.requestHandler(router).listen(res -> {
            if(res.succeeded()){
                LOG.info("Http Server 启动成功,占用[{}]端口",res.result().actualPort());
                this.server = localServer;
                startPromise.complete();
            }else {
                LOG.error("Http Server 启动失败",res.cause());
                startPromise.fail(res.cause());
                throw new IllegalStateException(res.cause());
            }
        });
    }


    @Override
    public void stop(Promise<Void> stopPromise) throws Exception {
        this.server.close(res -> {
            if(res.succeeded()){
                this.server = null;
                stopPromise.complete();
                LOG.info("Http Server 关闭成功");
            }else{
                stopPromise.fail(res.cause());
                LOG.error("Http Server 关闭失败",res.cause());
            }
        });
    }
}
